--- /dev/null
+/*
+
+ Support for Microsoft AutoRoute 2002 ".axe" files,
+
+ Copyright (C) 2005 Olaf Klein, o.b.klein@t-online.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+*/
+
+#include "defs.h"
+#include "jeeps/gpsmath.h"
+#include <ctype.h>
+
+#define MYNAME "msroute"
+
+#undef OLE_DEBUG
+
+FILE *fin;
+char *fin_name;
+
+static arglist_t msroute_args[] =
+{
+ {0, 0, 0, 0, 0 }
+};
+
+/* MS-AutoRoute structures */
+
+typedef struct msroute_head_s
+{
+ gbuint32 U1; /* 58/02/00/00 */
+ char masm[4]; /* "MASM " */
+ gbuint32 U2;
+ gbuint32 U3;
+ gbuint32 waypts;
+ gbuint32 U5;
+ gbuint32 U6;
+ gbuint32 U7;
+ gbuint32 U8;
+ gbuint32 U9;
+ gbuint32 U10;
+ gbuint32 U11;
+ gbuint32 U12;
+ gbuint32 U13;
+ gbuint32 U14;
+ gbuint32 U15;
+ gbuint32 U16;
+// short U17;
+// char U18;
+} msroute_head_t;
+
+#define MSROUTE_OBJ_NAME "Journey"
+
+/* simple ole file reader */
+
+#define OLE_MAX_NAME_LENGTH 32
+#define OLE_HEAD_FAT1_CT (512-0x4c)/4
+
+#define BLOCKS(a, b) (((a) + (b) - 1) / (b))
+
+static const char ole_magic[8] =
+{
+ 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1
+};
+
+/*
+ The ole implementation looks like a FAT filesystem.
+ Thatswhy i use in code fat1 as item for the "big blocks" or bbd
+ and fat2 for "small blocks" (sbd).
+
+ Remarks:
+
+ * in the moment ole_size1 and sector_size represents the same value
+ * in OLE_DEBUG mode: successfully tested with 64MB++ standard MS doc's (PowerPoint, Word)
+*/
+
+typedef struct ole_head_s
+{
+ char magic[8];
+ char clsid[16];
+ gbuint16 rev; /* offset 0x18 */
+ gbuint16 ver; /* offset 0x1a */
+ gbuint16 byte_order; /* offset 0x1c */
+ gbuint16 fat1_size_shift; /* offset 0x1e */
+ gbuint16 fat2_size_shift; /* offset 0x20 */
+ gbuint16 U7; /* offset 0x22 */
+ gbuint32 U8; /* offset 0x24 */
+ gbuint32 U9; /* offset 0x28 */
+ gbuint32 fat1_blocks; /* offset 0x2c */
+ gbuint32 prop_start; /* offset 0x30 */
+ gbuint32 U12; /* offset 0x34 */
+ gbuint32 fat1_min_size; /* offset 0x38 */
+ gbuint32 fat2_start; /* offset 0x3c */
+ gbuint32 fat2_blocks; /* offset 0x40 */
+ gbuint32 fat1_extra_start; /* offset 0x44 */
+ gbuint32 fat1_extra_ct; /* offset 0x48 */
+ gbuint32 fat1[OLE_HEAD_FAT1_CT]; /* offset 0x4c */
+} ole_head_t;
+
+typedef struct ole_prop_s
+{
+ gbuint16 name[32];
+ gbuint16 name_size; /* offset 0x40 */
+ char ole_typ; /* offset 0x42 */
+ char U1; /* offset 0x43 */
+ gbuint32 previous; /* offset 0x44 */
+ gbuint32 next; /* offset 0x48 */
+ gbuint32 dir; /* offset 0x4c */
+ gbuint32 U5; /* offset 0x50 */
+ gbuint32 U6; /* offset 0x54 */
+ gbuint32 U7; /* offset 0x58 */
+ gbuint32 U8; /* offset 0x5c */
+ gbuint32 U9; /* offset 0x60 */
+ gbuint32 U10; /* offset 0x64 */
+ gbuint32 U11; /* offset 0x68 */
+ gbuint32 U12; /* offset 0x6c */
+ gbuint32 U13; /* offset 0x70 */
+ gbuint32 first_sector; /* offset 0x74 */
+ gbuint32 length; /* offset 0x78 */
+ gbuint32 U16; /* offset 0x7c */
+} ole_prop_t;
+
+#define DIR_ITEM_SIZE sizeof(ole_prop_t)
+
+static int sector_size = 512;
+
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#define max(a,b) ((a) > (b)) ? (a) : (b)
+
+static int *ole_fat1 = NULL;
+static int *ole_fat2 = NULL;
+static int ole_fat1_ct;
+static int ole_fat2_ct;
+static int ole_size1;
+static int ole_size2;
+static int ole_size1_min = 4096;
+static ole_prop_t *ole_dir = NULL;
+static int ole_dir_ct;
+static ole_prop_t *ole_root = NULL;
+static char **ole_root_sec = NULL;
+static int ole_root_sec_ct;
+
+/* local helpers */
+
+static void
+is_fatal(const int condition, const char *fmt, ...)
+{
+ va_list args;
+ char buff[128];
+
+ if (condition == 0) return;
+
+ va_start(args, fmt);
+ vsnprintf(buff, sizeof(buff), fmt, args);
+ va_end(args);
+
+ fatal(MYNAME ": %s\n", buff);
+}
+
+static void
+print_buff(const char *buff, int count, const char *comment)
+{
+ int i;
+ printf(MYNAME ": dump of %s : ", comment);
+ for (i = 0; i < count; i++)
+ {
+ printf("%02x ", buff[i] & 0xFF);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+static void
+le_read32_buff(int *buff, const int count)
+{
+ int i;
+ for (i = 0; i < count; i++)
+ buff[i] = le_read32(&buff[i]);
+}
+
+/* simple OLE file reader */
+
+static void
+ole_read_sector(const int sector, void *target)
+{
+ int res;
+
+ res = fseek(fin, (sector + 1) * sector_size, SEEK_SET);
+ is_fatal((res != 0), "Could not seek file to sector %d!", sector + 1);
+ res = fread(target, 1, sector_size, fin);
+ is_fatal((res < sector_size), "Read error (%d, sector %d) on file \"%s\"!", res, sector, fin_name);
+}
+
+static ole_prop_t *
+ole_find_property(const char *property)
+{
+ int i;
+
+ for (i = 0; i < ole_dir_ct; i++)
+ {
+ int j, len;
+ char buff[OLE_MAX_NAME_LENGTH + 1];
+ ole_prop_t *item;
+
+ item = &ole_dir[i];
+ len = min(OLE_MAX_NAME_LENGTH, item->name_size / 2);
+
+ for (j = 0; j < len; j++)
+ buff[j] = item->name[j];
+ buff[j] = '\0';
+
+ if (case_ignore_strcmp(buff, property) == 0)
+ return item;
+ }
+ is_fatal((1), "\"%s\" not in property catalog!", property);
+}
+
+static char *
+ole_read_stream(const ole_prop_t *property)
+{
+ const char *action = "ole_read_stream";
+ int len, sector, big, blocksize, offs, left;
+ int i;
+ int *fat;
+ char *buff;
+
+ len = property->length;
+
+ if (len >= ole_size1_min)
+ {
+ big = 1;
+ blocksize = ole_size1;
+ fat = ole_fat1;
+ }
+ else
+ {
+ big = 0;
+ blocksize = ole_size2;
+ fat = ole_fat2;
+ }
+
+ offs = 0;
+ left = len;
+ sector = property->first_sector;
+
+ i = ((len + blocksize - 1) / blocksize) * blocksize;
+ buff = xmalloc(i); /* blocksize aligned */
+
+ if (big != 0)
+ {
+ while (left > 0)
+ {
+ int bytes = (left <= blocksize) ? left : blocksize;
+ ole_read_sector(sector, buff + offs);
+ left -= bytes;
+ offs += bytes;
+ if (left > 0)
+ {
+ sector = fat[sector];
+ is_fatal((sector < 0), "Broken stream (%s)!", action);
+ }
+ }
+ }
+ else
+ {
+ int chain = sector;
+ int blocks = (len + blocksize - 1) / blocksize;
+ int blocks_per_sector = sector_size / blocksize;
+
+ offs = 0;
+
+ while (blocks-- > 0)
+ {
+ char *temp;
+ int block_offs;
+
+ is_fatal((chain < 0), "Broken stream (%s)!", action);
+
+ sector = chain / blocks_per_sector;
+ is_fatal((sector >= ole_root_sec_ct), "Broken stream (%s)!", action);
+
+ temp = ole_root_sec[sector];
+ is_fatal((temp == NULL), "Broken stream (%s)!", action);
+
+ block_offs = (chain % blocks_per_sector) * blocksize;
+
+ memcpy(buff + offs, temp + block_offs, blocksize);
+
+ offs += blocksize;
+ chain = fat[chain];
+ }
+ }
+ return buff;
+}
+
+
+static char *
+ole_read_property_stream(const char *property_name, int *length)
+{
+ ole_prop_t *property;
+ char *result;
+
+ if ((property = ole_find_property(property_name)) == NULL) return NULL;
+
+ result = ole_read_stream(property);
+ if ((result != NULL) && (length != NULL))
+ *length = property->length;
+
+ return result;
+}
+
+#ifdef OLE_DEBUG
+
+static void
+ole_test_properties()
+{
+ int i;
+
+ for (i = 0; i < ole_dir_ct; i++)
+ {
+ char *temp;
+ char name[OLE_MAX_NAME_LENGTH + 1];
+ ole_prop_t *p = &ole_dir[i];
+
+ if ((p->ole_typ != 1) && (p->ole_typ != 2) && (p->ole_typ != 5)) continue;
+ if ((p->length <= 0) || (p->name_size <= 0)) continue;
+
+ temp = cet_str_uni_to_utf8(&p->name, min(p->name_size / 2, OLE_MAX_NAME_LENGTH));
+ strncpy(name, temp, sizeof(name));
+ xfree(temp);
+
+ printf(MYNAME ": ole_test_properties for \"%s\" (%d bytes):", name, p->length);
+
+ if ((case_ignore_strcmp(name, "Root Entry") == 0) ||
+ (p->length < ole_size1_min))
+ {
+ printf(" skipped...\n");
+ continue;
+ }
+ else
+ {
+ int sector = p->first_sector;
+ int length = p->length;
+ int block_size = ole_size1; /* sector_size */
+
+ printf("\n");
+
+ while ((length > 0) && (sector >= 0))
+ {
+ int bytes = (length > block_size) ? block_size : length;
+ int prev = sector;
+
+ length -= bytes;
+ sector = ole_fat1[sector];
+ if (sector == -3)
+ {
+ printf(MYNAME ": special block at %d\n", prev);
+ if ((prev + 2) < ole_fat1_ct)
+ sector = ole_fat1[prev + 1];
+ printf(MYNAME "-new sector: %d\n", sector);
+ }
+ }
+ is_fatal((length != 0), "Error in fat1 chain, sector = %d, %d bytes (=%d blocks) left!",
+ sector, length, BLOCKS(length, block_size));
+ }
+ }
+}
+#endif
+
+static void
+ole_init(void)
+{
+ ole_head_t head;
+ int i, i_offs, sector, count, left;
+ int fat1_extra[128];
+
+ ole_fat1 = NULL;
+ ole_fat2 = NULL;
+
+ sector_size = 512; /* fixed for the moment */
+
+ is_fatal((sizeof(head) != sector_size), "(!) internal error - invalid header size (%d)!", sizeof(head));
+
+ memset(&head, 0, sizeof(head));
+ fread(&head, sizeof(head), 1, fin);
+
+ is_fatal((strncmp(head.magic, ole_magic, sizeof(ole_magic)) != 0), "No MS document.");
+
+ head.rev = le_read16(&head.rev);
+ head.ver = le_read16(&head.ver);
+ head.byte_order = le_read16(&head.byte_order);
+ head.fat1_size_shift = le_read16(&head.fat1_size_shift);
+ head.fat2_size_shift = le_read16(&head.fat2_size_shift);
+ head.fat1_blocks = le_read32(&head.fat1_blocks);
+ head.prop_start = le_read32(&head.prop_start);
+ head.fat1_min_size = le_read32(&head.fat1_min_size);
+ head.fat2_start = le_read32(&head.fat2_start);
+ head.fat2_blocks = le_read32(&head.fat2_blocks);
+ head.fat1_extra_start = le_read32(&head.fat1_extra_start);
+ head.fat1_extra_ct = le_read32(&head.fat1_extra_ct);
+ le_read32_buff(&head.fat1[0], OLE_HEAD_FAT1_CT);
+
+ ole_size1 = (1 << head.fat1_size_shift);
+ ole_size2 = (1 << head.fat2_size_shift);
+ ole_size1_min = head.fat1_min_size;
+
+#ifdef OLE_DEBUG
+ printf(MYNAME "-head: (version.revision) = %d.%d\n", head.ver, head.rev);
+ printf(MYNAME "-head: byte-order = %d\n", head.byte_order);
+ printf(MYNAME "-head: big fat start sector = %d (0x%x)\n", head.fat1[0], (head.fat1[0] + 1) * 512);
+ printf(MYNAME "-head: big fat blocks = %d\n", head.fat1_blocks);
+ printf(MYNAME "-head: big fat block size = %d\n", (1 << head.fat1_size_shift));
+ printf(MYNAME "-head: small fat start sector = %d\n", head.fat2_start);
+ printf(MYNAME "-head: small fat blocks = %d\n", head.fat2_blocks);
+ printf(MYNAME "-head: small fat block size = %d\n", (1 << head.fat2_size_shift));
+ printf(MYNAME "-head: big fat minimum length = %d\n", head.fat1_min_size);
+ printf(MYNAME "-head: property catalog start sector = %d\n", head.prop_start);
+ printf(MYNAME "-head: additional big fat blocks = %d\n", head.fat1_extra_ct);
+ printf(MYNAME "-head: additional big fat start sector = %d (0x%x)\n", head.fat1_extra_start, (head.fat1_extra_start + 1) * 512);
+#endif
+
+ is_fatal((head.byte_order != -2), "Unsupported byte-order %d", head.byte_order);
+#if 0
+ sector_size = ole_size1; /* i'll implement this, if i get an MS-doc (ole) */
+ /* with "sector_size" other than 512 */
+#else
+ is_fatal((ole_size1 != 512), "Unsupported sector size %d", ole_size1);
+#endif
+ ole_fat1 = xmalloc(head.fat1_blocks * sector_size);
+ ole_fat1_ct = (head.fat1_blocks * sector_size) / sizeof(int);
+
+#ifdef OLE_DEBUG
+ printf(MYNAME "-big fat: %d maximum sectors, size in memory %d, max. datasize %d bytes\n",
+ ole_fat1_ct, head.fat1_blocks * sector_size, head.fat1_blocks * sector_size * sector_size / sizeof(int));
+#endif
+
+ i_offs = 0; /* load "big fat" into memory */
+ left = head.fat1_blocks;
+ count = (left > OLE_HEAD_FAT1_CT) ? OLE_HEAD_FAT1_CT : left;
+
+ for (i = 0; i < count; i++)
+ {
+ sector = head.fat1[i];
+ ole_read_sector(sector, &ole_fat1[i_offs]);
+ i_offs += ole_size1 / 4;
+ }
+
+ left -= count;
+
+ if (left > 0)
+ {
+ sector = head.fat1_extra_start;
+
+ while ((left > 0) && (sector >= 0))
+ {
+ ole_read_sector(sector, &fat1_extra);
+ le_read32_buff(&fat1_extra[0], 128);
+
+ count = (left < 127) ? left : 127;
+ for (i = 0; i < count; i++)
+ {
+ ole_read_sector(fat1_extra[i], &ole_fat1[i_offs]);
+ i_offs += ole_size1 / 4;
+ }
+ left -= count;
+ if (left > 0)
+ sector = fat1_extra[127];
+ }
+ is_fatal((left > 0), "Broken stream!");
+ }
+ if (ole_fat1_ct > 0)
+ le_read32_buff(&ole_fat1[0], ole_fat1_ct);
+
+
+ /* load fat2 "small fat" into memory */
+
+ sector = head.fat2_start;
+ if (sector >= 0)
+ {
+ count = 0;
+ do
+ {
+ if (ole_fat2 == NULL)
+ ole_fat2 = (int *)xmalloc((count + 1) * sector_size);
+ else
+ ole_fat2 = (int *)xrealloc(ole_fat2, (count + 1) * sector_size);
+
+ ole_read_sector(sector, (char *)ole_fat2 + (count * sector_size));
+ sector = ole_fat1[sector];
+
+ count++;
+ }
+ while (sector >= 0);
+
+ ole_fat2_ct = (count * sector_size) / sizeof(int);
+ if (ole_fat2_ct > 0)
+ le_read32_buff(&ole_fat2[0], ole_fat2_ct);
+ }
+
+ /* load directory (property catalog) */
+
+ sector = head.prop_start;
+ is_fatal((sector < 0), "Invalid file (no property catalog)!");
+
+ count = 0;
+ while (sector >= 0)
+ {
+ if (ole_dir == NULL)
+ ole_dir = (void *)xmalloc((count + 1) * sector_size);
+ else
+ ole_dir = (void *)xrealloc(ole_dir, (count + 1) * sector_size);
+
+ ole_read_sector(sector, (char *)ole_dir + (count * sector_size));
+ sector = ole_fat1[sector];
+
+ count++;
+ }
+ ole_dir_ct = (count * sector_size) / sizeof(ole_prop_t);
+
+ /* fix endianess of property catalog */
+
+ for (i = 0; i < ole_dir_ct; i++)
+ {
+ ole_prop_t *item = &ole_dir[i];
+
+ item->first_sector = le_read32(&item->first_sector);
+ item->length = le_read32(&item->length);
+ }
+
+ ole_root = ole_find_property("Root Entry");
+
+ /* read fat2 data sectors given by "Root Entry" */
+
+ ole_root_sec_ct = (ole_root->length + (sector_size - 1)) / sector_size;
+ ole_root_sec = xcalloc(ole_root_sec_ct + 1, sizeof(char *));
+
+ i = 0;
+ sector = ole_root->first_sector;
+ while (sector >= 0)
+ {
+ char *temp;
+
+ temp = ole_root_sec[i++] = xmalloc(sector_size);
+
+ ole_read_sector(sector, temp);
+ sector = ole_fat1[sector];
+ }
+#ifdef OLE_DEBUG
+ ole_test_properties();
+#endif
+}
+
+static void
+ole_deinit(void)
+{
+ if (ole_root_sec != NULL)
+ {
+ int i;
+ for (i = 0; i < ole_root_sec_ct; i++)
+ {
+ char *c;
+ if ((c = ole_root_sec[i])) xfree(c);
+ }
+ xfree(ole_root_sec);
+ }
+ if (ole_fat1 != NULL) xfree(ole_fat1);
+ if (ole_fat2 != NULL) xfree(ole_fat2);
+ if (ole_dir != NULL) xfree(ole_dir);
+}
+
+/* global MS AutoRoute functions */
+
+static void
+msroute_read_journey(void)
+{
+ int len;
+ char *buff;
+
+ buff = ole_read_property_stream(MSROUTE_OBJ_NAME, &len);
+
+ if ((buff != NULL) && (len > 0))
+ {
+ msroute_head_t *head = (msroute_head_t *)buff;
+ char *cin;
+ int len;
+ char text[256];
+ int count = 0;
+ route_head *route;
+ waypoint *wpt;
+
+ is_fatal((strncmp(head->masm, "MASM", 4) != 0), "Invalid or unknown data!");
+
+ cin = buff + 71; // sizeof(msroute_head_t);
+
+ route = route_head_alloc();
+ route_add_head(route);
+
+ head->waypts = le_read32(&head->waypts);
+
+ while (count < head->waypts)
+ {
+ double lat, lon;
+ short test;
+
+ /* tested, but undocumented */
+ is_fatal(((*cin != 0x23) && (*cin != 0x24)), "Invalid or unknown data!");
+ cin++;
+
+ len = *cin++;
+ strncpy(text, cin, len);
+ text[len] = '\0';
+
+ cin += len + 1;
+ test = le_read16(cin);
+ is_fatal((test != -2), "Unsupported byte order within data (%d).", test);
+
+ cin += 2;
+
+ len = *cin; /* skip wide-string 'name' */
+ cin += (len * 2) + 1;
+
+ cin += (5 * sizeof(int)); /* five unknown DWORDs */
+
+ /* offs 12 !!!! Latitude int32 LE */
+ /* offs 16 !!!! Longitude int32 LE */
+
+ lat = GPS_Math_Semi_To_Deg(le_read32(cin+12));
+ lon = GPS_Math_Semi_To_Deg(le_read32(cin+16));
+
+ cin += (23 * sizeof(int));
+ cin += 3;
+
+ count++;
+
+ wpt = waypt_new();
+
+ wpt->latitude = lat;
+ wpt->longitude = lon;
+ wpt->shortname = xstrdup(text);
+#ifdef OLE_DEBUG
+ waypt_add(waypt_dupe(wpt)); /* put to wpt-list to see results if no output is specified */
+#endif
+ route_add_wpt(route, wpt);
+ }
+ }
+
+ if (buff != NULL)
+ xfree(buff);
+}
+
+/* registered callbacks */
+
+static void msroute_rd_init(const char *fname)
+{
+ fin_name = xstrdup(fname);
+ fin = xfopen(fname, "rb", MYNAME);
+
+ ole_init();
+}
+
+static void msroute_rd_deinit(void)
+{
+ ole_deinit();
+
+ xfree(fin_name);
+ fclose(fin);
+}
+
+static void msroute_read(void)
+{
+ msroute_read_journey();
+}
+
+ff_vecs_t msroute_vecs = {
+ ff_type_file,
+ { ff_cap_none, ff_cap_none, ff_cap_read },
+ msroute_rd_init,
+ NULL,
+ msroute_rd_deinit,
+ NULL,
+ msroute_read,
+ NULL,
+ NULL,
+ msroute_args,
+ CET_CHARSET_MS_ANSI, 1 /* CET-REVIEW */
+};